#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include "include/mymodule.h"

// Importamos la tabla de llamadas a sistema 
extern void* sys_call_table[];

// Declaramos un parametro pid
int pid=1;
module_param (pid, int, 0);
MODULE_PARM_DESC (pid, "Process ID to monitor (default current)");

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Miguel Delgado, David Gonzalez");
MODULE_DESCRIPTION("proso_driver");


// Obtenim els cicles actuals
#define proso_rdtsc(low,high) \
__asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high));
static inline unsigned long long proso_get_cycles (void){
  unsigned long eax, edx;
  proso_rdtsc(eax, edx);
  return ((unsigned long long) edx << 32) + eax;
}
//


/*
* Inicialització del modul.
*/

static int __init Mymodule_init(void)               
{
   if(pid==1){pid = current->pid;}
   //Codi d’inicialització 
   printk(KERN_DEBUG "Mymodule carregat amb exit\n");

// Guardamos las llamadas origunales de la sys_call_table
antiguas[OPEN]=sys_call_table[POS_SYSCALL_OPEN];
antiguas[WRITE]=sys_call_table[POS_SYSCALL_WRITE];
antiguas[CLONE]=sys_call_table[POS_SYSCALL_CLONE];
antiguas[CLOSE]=sys_call_table[POS_SYSCALL_CLOSE];
antiguas[LSEEK]=sys_call_table[POS_SYSCALL_LSEEK];


// Definimos el vector locales con las funciones del modulo
locales[OPEN]=sys_open_local;
locales[WRITE]=sys_write_local;
locales[CLONE]=sys_clone_local;
locales[CLOSE]=sys_close_local;
locales[LSEEK]=sys_lseek_local;

// Redefinimos la sys_call_table con las funciones locales
sys_call_table[POS_SYSCALL_OPEN]=locales[OPEN];
sys_call_table[POS_SYSCALL_WRITE]=locales[WRITE];
sys_call_table[POS_SYSCALL_CLONE]=locales[CLONE];
sys_call_table[POS_SYSCALL_CLOSE]=locales[CLOSE];
sys_call_table[POS_SYSCALL_LSEEK]=locales[LSEEK];


   return 0;
   // Aquesta funció retorna 0 si tot ha
   // anat correctament i < 0 en cas d’error
}

/*
* Descarrega el módul.
*/
                                      
static void __exit Mymodule_exit(void)
{
printk("Mymodule Descarregat amb exit\n");
/* Codi de finalització */

// Restauramos las llamadas de la sys_call_table
sys_call_table[POS_SYSCALL_OPEN]=antiguas[OPEN];
sys_call_table[POS_SYSCALL_WRITE]=antiguas[WRITE];
sys_call_table[POS_SYSCALL_CLONE]=antiguas[CLONE];
sys_call_table[POS_SYSCALL_CLOSE]=antiguas[CLOSE];
sys_call_table[POS_SYSCALL_LSEEK]=antiguas[LSEEK];

imprimir_est(pid);
}



long sys_open_local(const char __user * filename, int flags, int mode)
{

long (*llam)(const char __user * filename, int flags, int mode);

long res;
unsigned long long ciclos_old;
unsigned long long ciclos_new;
unsigned long long tiempo;
struct my_thread_info* mithread;
int pidproc;

llam = antiguas[OPEN];
try_module_get(THIS_MODULE);

mithread=(struct my_thread_info*)current_thread_info();
pidproc = current->pid;

// Comprovamos que el proceso en ejecución tiene las estadisticas init
if(pidproc != mithread->pid){
reset_info(pidproc);
}

mithread->estadistiques.est[OPEN].num_entrades++;
// Calculamos el tiempo antes de la ejecución
ciclos_old=proso_get_cycles();

// Ejecutamos la función antigua
res=llam(filename, flags, mode);

// Calculamos el tiempo de ejecución
ciclos_new=proso_get_cycles();

tiempo=ciclos_new-ciclos_old;

// Miramos si se ha ejecutado correctamente
if(res>=0){mithread->estadistiques.est[OPEN].num_sortides_ok++;}
else{mithread->estadistiques.est[OPEN].num_sortides_error++;}

mithread->estadistiques.est[OPEN].durada_total+=tiempo;

module_put(THIS_MODULE);

return res;
}



ssize_t sys_write_local(unsigned int fd, const char __user * buf, size_t count)
{

ssize_t (*llam)(unsigned int fd, const char __user * buf, size_t count);

ssize_t res;
unsigned long long ciclos_old;
unsigned long long ciclos_new;
unsigned long long tiempo;
struct my_thread_info* mithread;
int pidproc;

try_module_get(THIS_MODULE);
llam=antiguas[WRITE];

mithread=(struct my_thread_info*)current_thread_info();
pidproc = current->pid;

// Comprovamos que el proceso en ejecución tiene las estadisticas init
if(pidproc != mithread->pid){
reset_info(pidproc);
}

// Miramos si se ha ejecutado correctamente
mithread->estadistiques.est[WRITE].num_entrades++;
ciclos_old=proso_get_cycles();

res=llam(fd, buf, count);

ciclos_new=proso_get_cycles();

tiempo=ciclos_new-ciclos_old;

if(res>=0){mithread->estadistiques.est[WRITE].num_sortides_ok++;}
else{mithread->estadistiques.est[WRITE].num_sortides_error++;}

mithread->estadistiques.est[WRITE].durada_total+=tiempo;

module_put(THIS_MODULE);

return res;
}


int sys_clone_local(struct pt_regs regs){

int (*llam)(struct pt_regs regs);
int res;
unsigned long long ciclos_old;
unsigned long long ciclos_new;
unsigned long long tiempo;
struct my_thread_info* mithread;
int pidproc;

llam=antiguas[CLONE];

try_module_get(THIS_MODULE);

mithread=(struct my_thread_info*)current_thread_info();
pidproc = current->pid;

// Comprovamos que el proceso en ejecución tiene las estadisticas init
if(pidproc != mithread->pid){
reset_info(pidproc);
}

mithread->estadistiques.est[CLONE].num_entrades++;
ciclos_old=proso_get_cycles();

res=llam(regs);

ciclos_new=proso_get_cycles();

tiempo=ciclos_new-ciclos_old;

// Miramos si se ha ejecutado correctamente
if(res>=0){mithread->estadistiques.est[CLONE].num_sortides_ok++;}
else{mithread->estadistiques.est[CLONE].num_sortides_error++;}

mithread->estadistiques.est[CLONE].durada_total+=tiempo;

module_put(THIS_MODULE);
return res;
}



long sys_close_local(unsigned int fd){

long (*llam)(unsigned int fd);
long res;
unsigned long long ciclos_old;
unsigned long long ciclos_new;
unsigned long long tiempo;
struct my_thread_info* mithread;
int pidproc;
try_module_get(THIS_MODULE);

llam=antiguas[CLOSE];

mithread=(struct my_thread_info*)current_thread_info();
pidproc = current->pid;

// Comprovamos que el proceso en ejecución tiene las estadisticas init
if(pidproc != mithread->pid){
reset_info(pidproc);
}

mithread->estadistiques.est[CLOSE].num_entrades++;

ciclos_old=proso_get_cycles();

res=llam(fd);

ciclos_new=proso_get_cycles();

tiempo=ciclos_new-ciclos_old;

// Miramos si se ha ejecutado correctamente
if(res>=0){mithread->estadistiques.est[CLOSE].num_sortides_ok++;}
else{mithread->estadistiques.est[CLOSE].num_sortides_error++;}

mithread->estadistiques.est[CLOSE].durada_total+=tiempo;

module_put(THIS_MODULE);
return res;
}



off_t sys_lseek_local(unsigned int fd, off_t offset, unsigned int origin){

off_t (*llam)(unsigned int fd, off_t offset, unsigned int origin);
off_t res;
unsigned long long ciclos_old;
unsigned long long ciclos_new;
unsigned long long tiempo;
struct my_thread_info* mithread;
int pidproc;

try_module_get(THIS_MODULE);

llam=antiguas[LSEEK];


mithread=(struct my_thread_info*)current_thread_info();
pidproc = current->pid;

// Comprovamos que el proceso en ejecución tiene las estadisticas init
if(pidproc != mithread->pid){
reset_info(pidproc);
}

mithread->estadistiques.est[LSEEK].num_entrades++;

ciclos_old=proso_get_cycles();

res=llam(fd, offset,origin);

ciclos_new=proso_get_cycles();

tiempo=ciclos_new-ciclos_old;

// Miramos si se ha ejecutado correctamente
if(res>=0){mithread->estadistiques.est[LSEEK].num_sortides_ok++;}
else{mithread->estadistiques.est[LSEEK].num_sortides_error++;}

mithread->estadistiques.est[LSEEK].durada_total+=tiempo;

module_put(THIS_MODULE);
return res;

}



EXPORT_SYMBOL(reset_info);

int reset_info(int pd)
{

struct task_struct* t;
struct my_thread_info *my_info;
int i;

t = find_task_by_pid(pd);
if(t!=NULL){

my_info=(struct my_thread_info*) (t)->thread_info;
my_info->pid=t->pid;

i=0;
while(i<5){
my_info->estadistiques.est[i].num_entrades=0;
my_info->estadistiques.est[i].num_sortides_ok=0;
my_info->estadistiques.est[i].num_sortides_error=0;
my_info->estadistiques.est[i].durada_total=0;
i++;
}

}
else{
	return -1;
}
return 0;
}




void imprimir_est(int pd)
{
struct task_struct* tk;
struct my_thread_info* inf;
int i;

tk= (struct task_struct*)find_task_by_pid(pd);
if(tk!=NULL){

inf = (struct my_thread_info*) tk->thread_info;

printk("Estadísticas del proceso: %d\n",inf->pid); 

i=0;
while(i<5){

printk("Numero de usos %s: %d\n",funcs[i],inf->estadistiques.est[i].num_entrades);
printk("Numero de salidas correctas: %d\n",inf->estadistiques.est[i].num_sortides_ok);
printk("Numero de salidas incorrectas: %d\n",inf->estadistiques.est[i].num_sortides_error);
printk("Tiempo en ejecución: %lld\n\n",inf->estadistiques.est[i].durada_total);
i++;
}


}
else{printk("Error al imprimir estadisticas: El proceso %d esta muerto\n", pd);}
}

EXPORT_SYMBOL(activar_monitorizacion);
EXPORT_SYMBOL(desactivar_monitorizacion);

void activar_monitorizacion(int num){

if(num<0){

sys_call_table[POS_SYSCALL_OPEN]=locales[OPEN];
sys_call_table[POS_SYSCALL_WRITE]=locales[WRITE];
sys_call_table[POS_SYSCALL_CLONE]=locales[CLONE];
sys_call_table[POS_SYSCALL_CLOSE]=locales[CLOSE];
sys_call_table[POS_SYSCALL_LSEEK]=locales[LSEEK];
}
else{
sys_call_table[num_llam[num]]=locales[num];
}
}

void desactivar_monitorizacion(int num){

if(num<0){
sys_call_table[POS_SYSCALL_OPEN]=antiguas[OPEN];
sys_call_table[POS_SYSCALL_WRITE]=antiguas[WRITE];
sys_call_table[POS_SYSCALL_CLONE]=antiguas[CLONE];
sys_call_table[POS_SYSCALL_CLOSE]=antiguas[CLOSE];
sys_call_table[POS_SYSCALL_LSEEK]=antiguas[LSEEK];
}
else{
sys_call_table[num_llam[num]]=antiguas[num];
}



}

module_init(Mymodule_init);
module_exit(Mymodule_exit);

